 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.ide.dialogs;

 import java.net.URI ;

 import org.eclipse.core.filesystem.EFS;
 import org.eclipse.core.filesystem.IFileSystem;
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.ResourceAttributes;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.content.IContentDescription;
 import org.eclipse.jface.dialogs.ErrorDialog;
 import org.eclipse.jface.preference.FieldEditor;
 import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.jface.util.PropertyChangeEvent;
 import org.eclipse.osgi.util.TextProcessor;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Text;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.dialogs.PropertyPage;
 import org.eclipse.ui.ide.dialogs.ResourceEncodingFieldEditor;
 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
 import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
 import org.eclipse.ui.internal.ide.LineDelimiterEditor;

 /**
  * The ResourceInfoPage is the page that shows the basic info about the
  * resource.
  */
 public class ResourceInfoPage extends PropertyPage {

     private Button editableBox;

     private Button executableBox;

     private Button archiveBox;

     private Button derivedBox;

     private boolean previousReadOnlyValue;

     private boolean previousExecutableValue;

     private boolean previousArchiveValue;

     private boolean previousDerivedValue;

     private IContentDescription cachedContentDescription;

     private ResourceEncodingFieldEditor encodingEditor;

     private LineDelimiterEditor lineDelimiterEditor;

     private static String READ_ONLY = IDEWorkbenchMessages.ResourceInfo_readOnly;

     private static String EXECUTABLE = IDEWorkbenchMessages.ResourceInfo_executable;

     private static String ARCHIVE = IDEWorkbenchMessages.ResourceInfo_archive;

     private static String DERIVED = IDEWorkbenchMessages.ResourceInfo_derived;

     private static String TYPE_TITLE = IDEWorkbenchMessages.ResourceInfo_type;

     private static String LOCATION_TITLE = IDEWorkbenchMessages.ResourceInfo_location;

     private static String RESOLVED_LOCATION_TITLE = IDEWorkbenchMessages.ResourceInfo_resolvedLocation;

     private static String SIZE_TITLE = IDEWorkbenchMessages.ResourceInfo_size;

     private static String PATH_TITLE = IDEWorkbenchMessages.ResourceInfo_path;

     private static String TIMESTAMP_TITLE = IDEWorkbenchMessages.ResourceInfo_lastModified;

     private static String FILE_ENCODING_TITLE = IDEWorkbenchMessages.WorkbenchPreference_encoding;

     private static String CONTAINER_ENCODING_TITLE = IDEWorkbenchMessages.ResourceInfo_fileEncodingTitle;

     // Max value width in characters before wrapping
 private static final int MAX_VALUE_WIDTH = 80;

     /**
      * Create the group that shows the name, location, size and type.
      *
      * @param parent
      * the composite the group will be created in
      * @param resource
      * the resource the information is being taken from.
      * @return the composite for the group
      */
     private Composite createBasicInfoGroup(Composite parent, IResource resource) {

         Font font = parent.getFont();

         Composite basicInfoComposite = new Composite(parent, SWT.NULL);
         GridLayout layout = new GridLayout();
         layout.numColumns = 2;
         layout.marginWidth = 0;
         layout.marginHeight = 0;
         basicInfoComposite.setLayout(layout);
         GridData data = new GridData();
         data.verticalAlignment = GridData.FILL;
         data.horizontalAlignment = GridData.FILL;
         basicInfoComposite.setLayoutData(data);
         basicInfoComposite.setFont(font);

         // The group for path
 Label pathLabel = new Label(basicInfoComposite, SWT.NONE);
         pathLabel.setText(PATH_TITLE);
         GridData gd = new GridData();
         gd.verticalAlignment = SWT.TOP;
         pathLabel.setLayoutData(gd);
         pathLabel.setFont(font);

         // path value label
 Text pathValueText = new Text(basicInfoComposite, SWT.WRAP
                 | SWT.READ_ONLY);
         String pathString = TextProcessor.process(resource.getFullPath()
                 .toString());
         pathValueText.setText(pathString);
         gd = new GridData();
         gd.widthHint = convertWidthInCharsToPixels(MAX_VALUE_WIDTH);
         gd.grabExcessHorizontalSpace = true;
         gd.horizontalAlignment = GridData.FILL;
         pathValueText.setLayoutData(gd);
         pathValueText.setFont(font);
         pathValueText.setBackground(pathValueText.getDisplay().getSystemColor(
                 SWT.COLOR_WIDGET_BACKGROUND));

         // The group for types
 Label typeTitle = new Label(basicInfoComposite, SWT.LEFT);
         typeTitle.setText(TYPE_TITLE);
         typeTitle.setFont(font);

         Text typeValue = new Text(basicInfoComposite, SWT.LEFT | SWT.READ_ONLY);
         typeValue.setText(IDEResourceInfoUtils.getTypeString(resource,
                 getContentDescription(resource)));
         typeValue.setBackground(typeValue.getDisplay().getSystemColor(
                 SWT.COLOR_WIDGET_BACKGROUND));
         typeValue.setFont(font);

         // The group for location
 Label locationTitle = new Label(basicInfoComposite, SWT.LEFT);
         locationTitle.setText(LOCATION_TITLE);
         gd = new GridData();
         gd.verticalAlignment = SWT.TOP;
         locationTitle.setLayoutData(gd);
         locationTitle.setFont(font);

         Text locationValue = new Text(basicInfoComposite, SWT.WRAP
                 | SWT.READ_ONLY);
         String locationStr = TextProcessor.process(IDEResourceInfoUtils
                 .getLocationText(resource));
         locationValue.setText(locationStr);
         gd = new GridData();
         gd.widthHint = convertWidthInCharsToPixels(MAX_VALUE_WIDTH);
         gd.grabExcessHorizontalSpace = true;
         gd.horizontalAlignment = GridData.FILL;
         locationValue.setLayoutData(gd);
         locationValue.setFont(font);
         locationValue.setBackground(locationValue.getDisplay().getSystemColor(
                 SWT.COLOR_WIDGET_BACKGROUND));

         if (isPathVariable(resource)) {
             Label resolvedLocationTitle = new Label(basicInfoComposite,
                     SWT.LEFT);
             resolvedLocationTitle.setText(RESOLVED_LOCATION_TITLE);
             gd = new GridData();
             gd.verticalAlignment = SWT.TOP;
             resolvedLocationTitle.setLayoutData(gd);
             resolvedLocationTitle.setFont(font);

             Text resolvedLocationValue = new Text(basicInfoComposite, SWT.WRAP
                     | SWT.READ_ONLY);
             resolvedLocationValue.setText(IDEResourceInfoUtils
                     .getResolvedLocationText(resource));
             gd = new GridData();
             gd.widthHint = convertWidthInCharsToPixels(MAX_VALUE_WIDTH);
             gd.grabExcessHorizontalSpace = true;
             gd.horizontalAlignment = GridData.FILL;
             resolvedLocationValue.setLayoutData(gd);
             resolvedLocationValue.setFont(font);
             resolvedLocationValue.setBackground(resolvedLocationValue
                     .getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
         }
         if (resource.getType() == IResource.FILE) {
             // The group for size
 Label sizeTitle = new Label(basicInfoComposite, SWT.LEFT);
             sizeTitle.setText(SIZE_TITLE);
             sizeTitle.setFont(font);

             Text sizeValue = new Text(basicInfoComposite, SWT.LEFT
                     | SWT.READ_ONLY);
             sizeValue.setText(IDEResourceInfoUtils.getSizeString(resource));
             gd = new GridData();
             gd.widthHint = convertWidthInCharsToPixels(MAX_VALUE_WIDTH);
             gd.grabExcessHorizontalSpace = true;
             gd.horizontalAlignment = GridData.FILL;
             sizeValue.setLayoutData(gd);
             sizeValue.setFont(font);
             sizeValue.setBackground(sizeValue.getDisplay().getSystemColor(
                     SWT.COLOR_WIDGET_BACKGROUND));
         }

         return basicInfoComposite;
     }

     protected Control createContents(Composite parent) {

         PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(),
                 IIDEHelpContextIds.RESOURCE_INFO_PROPERTY_PAGE);

         // layout the page
 IResource resource = (IResource) getElement().getAdapter(
                 IResource.class);
         
         if (resource == null) {
             Label label = new Label(parent, SWT.NONE);
             label.setText(IDEWorkbenchMessages.ResourceInfoPage_noResource);
             return label;
         }
         
         if (resource.getType() != IResource.PROJECT) {
             ResourceAttributes attrs = resource.getResourceAttributes();
             if (attrs != null) {
                 previousReadOnlyValue = attrs.isReadOnly();
                 previousExecutableValue = attrs.isExecutable();
                 previousArchiveValue = attrs.isArchive();
             } else {
                 previousReadOnlyValue = previousExecutableValue = previousArchiveValue = false;
             }
             previousDerivedValue = resource.isDerived();
         }

         // top level group
 Composite composite = new Composite(parent, SWT.NONE);
         GridLayout layout = new GridLayout();
         layout.marginWidth = 0;
         layout.marginHeight = 0;
         composite.setLayout(layout);
         GridData data = new GridData(GridData.FILL);
         data.grabExcessHorizontalSpace = true;
         composite.setLayoutData(data);
         composite.setFont(parent.getFont());

         createBasicInfoGroup(composite, resource);
         createSeparator(composite);
         createStateGroup(composite, resource);
         new Label(composite, SWT.NONE); // a vertical spacer
 encodingEditor = new ResourceEncodingFieldEditor(
                 getFieldEditorLabel(resource), composite, resource);
         encodingEditor.setPage(this);
         encodingEditor.load();

         encodingEditor.setPropertyChangeListener(new IPropertyChangeListener() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
              */
             public void propertyChange(PropertyChangeEvent event) {
                 if (event.getProperty().equals(FieldEditor.IS_VALID)) {
                     setValid(encodingEditor.isValid());
                 }

             }
         });

         
         if (resource.getType() == IResource.PROJECT) {
             lineDelimiterEditor = new LineDelimiterEditor(composite, resource
                     .getProject());
             lineDelimiterEditor.doLoad();
         }
         
         //We can't save the preferences for close
 if (resource.getType() == IResource.PROJECT && !((IProject)resource).isOpen()){
             encodingEditor.setEnabled(false, composite);
             lineDelimiterEditor.setEnabled(false);
         }

         return composite;
     }

     /**
      * Return the label for the encoding field editor for the resource.
      *
      * @param resource -
      * the resource to edit.
      * @return String
      */
     private String getFieldEditorLabel(IResource resource) {
         if (resource instanceof IContainer) {
             return CONTAINER_ENCODING_TITLE;
         }
         return FILE_ENCODING_TITLE;
     }

     /**
      * Create the isEditable button and it's associated label as a child of
      * parent using the editableValue of the receiver. The Composite will be the
      * parent of the button.
      *
      * @param composite
      * the parent of the button
      */
     private void createEditableButton(Composite composite) {

         this.editableBox = new Button(composite, SWT.CHECK | SWT.RIGHT);
         this.editableBox.setAlignment(SWT.LEFT);
         this.editableBox.setText(READ_ONLY);
         this.editableBox.setSelection(this.previousReadOnlyValue);
         this.editableBox.setFont(composite.getFont());
         GridData data = new GridData();
         data.horizontalSpan = 2;
         this.editableBox.setLayoutData(data);
     }

     /**
      * Create the isExecutable button and it's associated label as a child of
      * parent using the editableValue of the receiver. The Composite will be the
      * parent of the button.
      *
      * @param composite
      * the parent of the button
      */
     private void createExecutableButton(Composite composite) {

         this.executableBox = new Button(composite, SWT.CHECK | SWT.RIGHT);
         this.executableBox.setAlignment(SWT.LEFT);
         this.executableBox.setText(EXECUTABLE);
         this.executableBox.setSelection(this.previousExecutableValue);
         this.executableBox.setFont(composite.getFont());
         GridData data = new GridData();
         data.horizontalSpan = 2;
         this.executableBox.setLayoutData(data);
     }

     /**
      * Create the isArchive button and it's associated label as a child of
      * parent using the editableValue of the receiver. The Composite will be the
      * parent of the button.
      *
      * @param composite
      * the parent of the button
      */
     private void createArchiveButton(Composite composite) {

         this.archiveBox = new Button(composite, SWT.CHECK | SWT.RIGHT);
         this.archiveBox.setAlignment(SWT.LEFT);
         this.archiveBox.setText(ARCHIVE);
         this.archiveBox.setSelection(this.previousArchiveValue);
         this.archiveBox.setFont(composite.getFont());
         GridData data = new GridData();
         data.horizontalSpan = 2;
         this.archiveBox.setLayoutData(data);
     }

     /**
      * Create the derived button and it's associated label as a child of parent
      * using the derived of the receiver. The Composite will be the parent of
      * the button.
      *
      * @param composite
      * the parent of the button
      */
     private void createDerivedButton(Composite composite) {

         this.derivedBox = new Button(composite, SWT.CHECK | SWT.RIGHT);
         this.derivedBox.setAlignment(SWT.LEFT);
         this.derivedBox.setText(DERIVED);
         this.derivedBox.setSelection(this.previousDerivedValue);
         this.derivedBox.setFont(composite.getFont());
         GridData data = new GridData();
         data.horizontalSpan = 2;
         this.derivedBox.setLayoutData(data);
     }

     /**
      * Create a separator that goes across the entire page
      *
      * @param composite
      * The parent of the seperator
      */
     private void createSeparator(Composite composite) {

         Label separator = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
         GridData gridData = new GridData();
         gridData.horizontalAlignment = GridData.FILL;
         gridData.grabExcessHorizontalSpace = true;
         separator.setLayoutData(gridData);
     }

     /**
      * Create the group that shows the read only state and the timestamp.
      *
      * @param parent
      * the composite the group will be created in
      * @param resource
      * the resource the information is being taken from.
      */
     private void createStateGroup(Composite parent, IResource resource) {

         Font font = parent.getFont();

         Composite composite = new Composite(parent, SWT.NULL);
         GridLayout layout = new GridLayout();
         layout.numColumns = 2;
         layout.marginWidth = 0;
         layout.marginHeight = 0;
         composite.setLayout(layout);
         GridData data = new GridData();
         data.horizontalAlignment = GridData.FILL;
         composite.setLayoutData(data);
         composite.setFont(font);

         Label timeStampLabel = new Label(composite, SWT.NONE);
         timeStampLabel.setText(TIMESTAMP_TITLE);
         timeStampLabel.setFont(font);

         // timeStamp value label
 Text timeStampValue = new Text(composite, SWT.READ_ONLY);
         timeStampValue.setText(IDEResourceInfoUtils
                 .getDateStringValue(resource));
         timeStampValue.setFont(font);
         timeStampValue.setBackground(timeStampValue.getDisplay()
                 .getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
         timeStampValue.setLayoutData(new GridData(GridData.FILL_HORIZONTAL
                 | GridData.GRAB_HORIZONTAL));

         // Not relevant to projects
 if (resource.getType() != IResource.PROJECT) {
             URI location = resource.getLocationURI();
             if (location != null && location.getScheme() != null) {
                 try {
                     IFileSystem fs = EFS.getFileSystem(location.getScheme());
                     int attributes = fs.attributes();
                     if ((attributes & EFS.ATTRIBUTE_READ_ONLY) != 0) {
                         createEditableButton(composite);
                     }
                     if ((attributes & EFS.ATTRIBUTE_EXECUTABLE) != 0) {
                         createExecutableButton(composite);
                     }
                     if ((attributes & EFS.ATTRIBUTE_ARCHIVE) != 0) {
                         createArchiveButton(composite);
                     }
                 } catch (CoreException e) {
                     // ignore if we can't access the file system for this
 // resource
 }
             }
             createDerivedButton(composite);
             // create warning for executable flag
 if (executableBox != null && resource.getType() == IResource.FOLDER) {
                 Composite noteComposite = createNoteComposite(font, composite,
                         IDEWorkbenchMessages.Preference_note,
                         IDEWorkbenchMessages.ResourceInfo_exWarning);
                 GridData noteData = new GridData();
                 noteData.horizontalSpan = 2;
                 noteComposite.setLayoutData(noteData);
             }
         }
     }

     private IContentDescription getContentDescription(IResource resource) {
         if (resource.getType() != IResource.FILE) {
             return null;
         }

         if (cachedContentDescription == null) {
             try {
                 cachedContentDescription = ((IFile) resource)
                         .getContentDescription();
             } catch (CoreException e) {
                 // silently ignore
 }
         }
         return cachedContentDescription;
     }

     /**
      * Returns whether the given resource is a linked resource bound to a path
      * variable.
      *
      * @param resource
      * resource to test
      * @return boolean <code>true</code> the given resource is a linked
      * resource bound to a path variable. <code>false</code> the given
      * resource is either not a linked resource or it is not using a
      * path variable.
      */
     private boolean isPathVariable(IResource resource) {
         if (!resource.isLinked()) {
             return false;
         }

         IPath resolvedLocation = resource.getLocation();
         if (resolvedLocation == null) {
             // missing path variable
 return true;
         }
         IPath rawLocation = resource.getRawLocation();
         if (resolvedLocation.equals(rawLocation)) {
             return false;
         }

         return true;
     }

     /**
      * Reset the editableBox to the false.
      */
     protected void performDefaults() {

         IResource resource = (IResource) getElement().getAdapter(
                 IResource.class);
         
         if (resource == null)
             return;

         // Nothing to update if we never made the box
 if (this.editableBox != null) {
             this.editableBox.setSelection(false);
         }

         // Nothing to update if we never made the box
 if (this.executableBox != null) {
             this.executableBox.setSelection(false);
         }

         // Nothing to update if we never made the box
 if (this.derivedBox != null) {
             this.derivedBox.setSelection(false);
         }

         encodingEditor.loadDefault();

         if (lineDelimiterEditor != null) {
             lineDelimiterEditor.loadDefault();
         }

     }

     /**
      * Apply the read only state and the encoding to the resource.
      */
     public boolean performOk() {

         IResource resource = (IResource) getElement().getAdapter(
                 IResource.class);
         
         if (resource == null)
             return true;

         encodingEditor.store();

         if (lineDelimiterEditor != null) {
             lineDelimiterEditor.store();
         }

         try {
             ResourceAttributes attrs = resource.getResourceAttributes();
             if (attrs != null) {
                 boolean hasChange = false;
                 // Nothing to update if we never made the box
 if (editableBox != null
                         && editableBox.getSelection() != previousReadOnlyValue) {
                     attrs.setReadOnly(editableBox.getSelection());
                     hasChange = true;
                 }
                 if (executableBox != null
                         && executableBox.getSelection() != previousExecutableValue) {
                     attrs.setExecutable(executableBox.getSelection());
                     hasChange = true;
                 }
                 if (archiveBox != null
                         && archiveBox.getSelection() != previousArchiveValue) {
                     attrs.setArchive(archiveBox.getSelection());
                     hasChange = true;
                 }
                 if (hasChange) {
                     resource.setResourceAttributes(attrs);
                     attrs = resource.getResourceAttributes();
                     if (attrs != null) {
                         previousReadOnlyValue = attrs.isReadOnly();
                         previousExecutableValue = attrs.isExecutable();
                         previousArchiveValue = attrs.isArchive();
                         if (editableBox != null) {
                             editableBox.setSelection(attrs.isReadOnly());
                         }
                         if (executableBox != null) {
                             executableBox.setSelection(attrs.isExecutable());
                         }
                         if (archiveBox != null) {
                             archiveBox.setSelection(attrs.isArchive());
                         }
                     }
                 }
             }

             // Nothing to update if we never made the box
 if (this.derivedBox != null) {
                 boolean localDerivedValue = derivedBox.getSelection();
                 if (previousDerivedValue != localDerivedValue) {
                     resource.setDerived(localDerivedValue);
                     boolean isDerived = resource.isDerived();
                     previousDerivedValue = isDerived;
                     derivedBox.setSelection(isDerived);
                 }
             }
         } catch (CoreException exception) {
             ErrorDialog.openError(getShell(),
                     IDEWorkbenchMessages.InternalError, exception
                             .getLocalizedMessage(), exception.getStatus());
             return false;
         }
         return true;
     }
 }

